#include "dri.h"
#include "ifs.h"
#include "fs.h"
#include <string.h>
#include "devfs.h"
#include "dbgio.h"
#include "kmm.h"

devfs_dir_t *devfs_list;
struct vfs_node *devfs_dir;
ifs_descriptor_t *devfs_ifs_desc;
dri_driver_descriptor_t *devfs_dri_desc;

struct vfs_node * devfs_mkstub(struct vfs_node *node,char *name,uint32_t mode){
	return 0;
}

void devfs_ocstub(struct vfs_node *node){
	return;
}

uint32_t devfs_read(struct vfs_node *node ,uint8_t *buffer ,uint32_t offset ,uint32_t length,uint32_t fflags){
	uint32_t result;
	devfs_file_t *file = (devfs_file_t *)node->impl;
	process_info_t *user_task = get_current_process();
	enter_module_code();
	result = file->read_hook(node,buffer,offset,length,fflags);
	exit_module_code();
	return result;
}

uint32_t devfs_write(struct vfs_node *node ,uint8_t *buffer ,uint32_t offset ,uint32_t length,uint32_t fflags){
	uint32_t result;
	devfs_file_t *file = (devfs_file_t *)node->impl;
	process_info_t *user_task = get_current_process();
	enter_module_code();
	result = file->write_hook(node,buffer,offset,length,fflags);
	exit_module_code();
	return result;
}

struct vfs_dir * devfs_readdir(struct vfs_node *node,uint32_t id){
	devfs_dir_t *rd_dir = (devfs_dir_t *) node->inode;
	LLIST *ll_f = kllist_get(rd_dir->files,id);
	if (ll_f == 0)
		return 0;
	vfs_node_t *node_f = (vfs_node_t *) ll_f->value;
	struct vfs_dir *dirent = (struct vfs_dir *)kmalloc(sizeof(vfs_dir_t));
	dirent->inode = node_f->inode;
	strcpy(dirent->name,node_f->name);
	return dirent;
}

struct vfs_node * devfs_finddir(struct vfs_node *node,char *name){
	devfs_dir_t *rd_dir = (devfs_dir_t *) node->inode;
	//dprintf("DEVFS_FINDDIR(%s)\n",name);
	for (uint32_t current = 0;current < rd_dir->length;current++){
		LLIST *ll_f = kllist_get(rd_dir->files,current);
	//	dprintf("0x%x",ll_f);
		if (ll_f == 0)
			continue;
		if (ll_f->value == 0)
			continue;
		vfs_node_t *node_f = (vfs_node_t *) ll_f->value;
	//	dprintf("0x%x 0x%x",node_f,ll_f);
	//	dprintf("%s == %s\n",name,node_f->name);
		if (strcmp(node_f->name,name) == 0)
			return node_f;
	}
	return 0;
}

void dri_register_specfile(char *name,uint32_t flags,uint32_t inode,uint32_t mode,read_fs_t read_hook,write_fs_t write_hook){
	/* Allocate memory */
	//dprintf("register specfile:%s\n",name);
	vfs_node_t *node = (vfs_node_t *) kmalloc(sizeof(vfs_node_t));
	LLIST *ll_file = (LLIST *) kmalloc(sizeof(LLIST));
//	dprintf("0x%x",ll_file);
	devfs_file_t *devfile = (devfs_file_t *) kmalloc(sizeof(devfs_file_t));
	/* Clear memory */
	memset((void *)node,0,sizeof(vfs_node_t));
	/* Set up devfile */
	devfile->driver_process = get_current_process();
	devfile->read_hook = read_hook;
	devfile->write_hook = write_hook;
	/*Set up file node*/	
	strcpy(node->name,name);
	node->gid = 0;
	node->uid = 0;
	node->mask = mode;
	node->flags = VFS_FILE_FLAG | flags;
	node->parent = node;
	node->length = 0;
	node->read = devfs_read;
	node->write = devfs_write;
	node->open = devfs_ocstub;
	node->close = devfs_ocstub;
	node->inode = inode;
	node->impl = (uint32_t) devfile;
	/* Add to DEV directory */
	ll_file->value = (void *) node;
	llist_add_end(devfs_list->files,ll_file);
	devfs_list->length++;
}

struct vfs_node * devfs_mount(struct vfs_node *special){
	return devfs_dir;
}

void devfs_umount(struct vfs_node *root){
}

void devfs_main(){
	/* Allocate memory */
	devfs_dir = (struct vfs_node *) kmalloc(sizeof(vfs_node_t));
	devfs_list = (devfs_dir_t *) kmalloc(sizeof(devfs_dir_t));
	devfs_list->files = (LLIST *) kmalloc(sizeof(LLIST));
	devfs_dri_desc = (dri_driver_descriptor_t *) kmalloc(sizeof(dri_driver_descriptor_t));
	devfs_ifs_desc = (ifs_descriptor_t *) kmalloc(sizeof(ifs_descriptor_t));
	/* Clear memory */
	memset((void *)devfs_dir,0,sizeof(vfs_node_t));
	/* Set up DEVFS node */
	strcpy(devfs_dir->name,"devfs_root");
	devfs_dir->gid = 0;//Kernel is always root
	devfs_dir->uid = 0;// ^
	devfs_dir->mask = 0;
	devfs_dir->flags = VFS_DIRECTORY_FLAG;
	devfs_dir->parent = 0;
	devfs_dir->length = 0;
	devfs_dir->finddir = devfs_finddir;
	devfs_dir->readdir = devfs_readdir;
	devfs_dir->touch   = devfs_mkstub;
	devfs_dir->mkdir   = devfs_mkstub;
	devfs_dir->inode   = (uint32_t) devfs_list;
	/* Set up devfs_list */
	llist_create(devfs_list->files);
	devfs_list->node = devfs_dir;
	devfs_list->length = 0;
	/* Set up IFS descriptor */
	strcpy(devfs_ifs_desc->ifs_name,"devfs");
	devfs_ifs_desc->ifs_id = 0xDEFF51F5;
	devfs_ifs_desc->ifs_mount_cb = devfs_mount;
	devfs_ifs_desc->ifs_umount_cb = devfs_umount;
	/* Set up DRI descriptor */
	strcpy(devfs_dri_desc->driver_name,"Special Node IFS Driver");
	devfs_dri_desc->driver_magic = DRI_DRIVER_MAGIC;
	devfs_dri_desc->driver_class = DRI_CLASS_IFS;
	devfs_dri_desc->driver_id    = devfs_ifs_desc->ifs_id;
	devfs_dri_desc->driver_class_desc = (void *) devfs_ifs_desc;
	devfs_dri_desc->init_sci = dri_syscall_initialize;
	devfs_dri_desc->driver_main = dri_driver_main_stub;
	/* Register driver with DRI */
	dri_register_driver(devfs_dri_desc);
}